using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Xml;

namespace DarkMatter.Chat
{
    public class ChatService : ServiceBase, IChatService
    {
        private readonly ChatUser _currentUser;
        private readonly Dictionary<IChannel, ChatUser> _channelLookup = new Dictionary<IChannel, ChatUser>();

        private readonly IMessageFactory _messageFactory;
        private readonly IMessageEncoder _messageEncoder;

        public ChatService(IMessageFactory messageFactory, IMessageEncoder messageEncoder)
        {
            _messageFactory = messageFactory;
            _messageEncoder = messageEncoder;
            _currentUser = messageFactory.CurrentUser;
        }

        public ChatUser CurrentUser
        {
            get { return _currentUser; }
        }

        public void Connect(IChannel channel)
        {
            channel.BytesRecieved += _channel_BytesRecieved;
            _channelLookup.Add(channel, null);

            if (channel.IsConnected)
            {
                channel.Send(_messageEncoder.Encode(_messageFactory.InfoMessage(ConnectedMessage, null)));
            }
        }

        void _channel_BytesRecieved(IChannel source, byte[] bytes)
        {
            Message message = _messageEncoder.Decode(bytes);

            if (message.MessageType == "INFO")
            {
                HandleInfoMessage(source, message);
            }
            else
            {
                FireMessageRecievedEvent(message);
            }
        }

        private void FireMessageRecievedEvent(Message message)
        {
            MessageRecievedHandler handler = MessageRecieved;
            if (handler != null)
            {
                foreach (MessageRecievedHandler h in handler.GetInvocationList())
                {
                    try
                    {
                        h.Invoke(message);
                    }
                    catch (Exception) { }
                }
            }
        }

        private void HandleInfoMessage(IChannel source, Message message)
        {
            string messageText;
            string value = "";
            if (message.ToString().Contains(":"))
            {
                int i = message.ToString().IndexOf(":");
                messageText = message.ToString().Substring(0, i);
                value = message.ToString().Substring(i + 1, message.ToString().Length - i - 1);
            }
            else
            {
                messageText = message.ToString();
            }
            switch (messageText)
            {
                case ConnectedMessage:
                    if (_channelLookup.ContainsKey(source) && _channelLookup[source] == null)
                    {
                        _channelLookup[source] = message.Source;
                    }
                    break;
                case ChatStatusChangedMessage:
                    if (_channelLookup.ContainsKey(source) && _channelLookup[source] != null)
                    {
                        if (value == ChatStatus.Available.ToString().ToUpper())
                        {
                            _channelLookup[source].Status = ChatStatus.Available;
                        }
                        else if (value == ChatStatus.Busy.ToString().ToUpper())
                        {
                            _channelLookup[source].Status = ChatStatus.Busy;
                        }
                    }
                    break;
            }
        }

        public void SendMessage(string text)
        {
            byte[] payload = _messageEncoder.Encode(_messageFactory.TextMessage(text));

            SendToAllChannels(payload);
        }

        private void SendToAllChannels(byte[] payload)
        {
            foreach (KeyValuePair<IChannel, ChatUser> channelKeyPair in _channelLookup)
            {
                IChannel channel = channelKeyPair.Key;
                channel.Send(payload);
            }
        }

        private const string ConnectedMessage = "CONNECTED";
        private const string ChatStatusChangedMessage = "CHAT_STATUS_CHANGED";

        public event MessageRecievedHandler MessageRecieved;

        private void ChangeChatStatus(ChatStatus status)
        {
            _currentUser.Status = status;
            byte[] payload = _messageEncoder.Encode(_messageFactory.InfoMessage(ChatStatusChangedMessage, status.ToString().ToUpper()));

            SendToAllChannels(payload);
        }

        public ChatStatus ChatStatus
        {
            get { return _currentUser.Status; }
            set { ChangeChatStatus(value); }
        }
    }
}